//https://docs.unity.cn/cn/current/ScriptReference/Rendering.StencilState.html

namespace FIREFLYX {
    /** Stencil操作选项 */
    export enum StencilOp {
        /** 保持当前值不变 */
        KEEP = WebGL2RenderingContext.KEEP,
        /** 设置为 0 */
        ZERO = WebGL2RenderingContext.ZERO,
        /** 替换为ref的值 */
        REPLACE = WebGL2RenderingContext.REPLACE,
        /**当前值 + ref的值*/
        INCR = WebGL2RenderingContext.INCR,
        /**当前值 + ref的值,如果到达最大值则回到0 */
        INCR_WRAP = WebGL2RenderingContext.INCR_WRAP,
        /**当前值 - ref的值*/
        DECR = WebGL2RenderingContext.DECR,
        /**当前值 - ref的值,如果到达0则回到最大值*/
        DECR_WRAP = WebGL2RenderingContext.DECR_WRAP,
        /** 进行按位取反操作 */
        INVERT = WebGL2RenderingContext.INVERT,
    }

    /** 模板状态 */
    export class StencilState {
        private static cacheStateMap: { [key: string]: StencilState } = {};
        private states: number[];
        // eslint-disable-next-line no-useless-constructor
        private constructor() {
            //
        }

        /** 比较函数 ( 正面 )*/
        public get funcFront(): CompareFunc { return this.states[0]; }
        /** 模板测试的参考值 ( 正面 )*/
        public get refFront(): number { return this.states[1]; }
        /** 测试时和参考值进行与运算 ( 正面 )*/
        public get valueMaskFront(): number { return this.states[2]; }
        /** 模板测试失败时 ( 正面 )*/
        public get failOpFront(): StencilOp { return this.states[3]; }
        /** 模板测试通过但深度测试失败时 ( 正面 )*/
        public get zFailOpFront(): StencilOp { return this.states[4]; }
        /** 模板测试和深度测试都通过时 ( 正面 )*/
        public get passOpFront(): StencilOp { return this.states[5]; }
        /** 在写入模板缓存时和参考值进行与运算 ( 正面 )*/
        public get writeMaskFront(): number { return this.states[6]; }

        /** 比较函数 ( 背面 )*/
        public get funcBack(): CompareFunc { return this.states[7]; }
        /** 模板测试的参考值 ( 背面 )*/
        public get refBack(): number { return this.states[8]; }
        /** 测试时和参考值进行与运算 ( 背面 )*/
        public get valueMaskBack(): number { return this.states[9]; }
        /** 模板测试失败时 ( 背面 )*/
        public get failOpBack(): StencilOp { return this.states[10]; }
        /** 模板测试通过但深度测试失败时 ( 背面 )*/
        public get zFailOpBack(): StencilOp { return this.states[11]; }
        /** 模板测试和深度测试都通过时 ( 背面 )*/
        public get passOpBack(): StencilOp { return this.states[12]; }
        /** 在写入模板缓存时和参考值进行与运算 ( 背面 )*/
        public get writeMaskBack(): number { return this.states[13]; }

        /**
         * 获取模板状态
         * @param func 比较函数
         * @param ref 模板测试的参考值
         * @param valueMask 在测试时和参考值进行与运算
         * @param failOp 指定模板测试失败时
         * @param zFailOp 指定当模板测试通过但深度测试失败时
         * @param passOp 指定当模板测试和深度测试都通过时
         * @param writeMask 在写入模板缓存时和参考值进行与运算
         * @returns 模板状态对象
         */
        public static getState(func = CompareFunc.ALWAYS, ref = 0, valueMask = 0xff, failOp = StencilOp.KEEP, zFailOp = StencilOp.KEEP, passOp = StencilOp.KEEP, writeMask = 0xff) {
            if (ref < 0) ref = 0;
            ref = Math.floor(ref);
            let stateKey = `${func}_${ref}_${valueMask}_${failOp}_${zFailOp}_${passOp}_${writeMask}`;
            let state = this.cacheStateMap[stateKey];
            if (!state) {
                this.cacheStateMap[stateKey] = state = new StencilState();
                state.states = [
                    //front face
                    func, ref, valueMask, failOp, zFailOp, passOp, writeMask,
                    //back face
                    func, ref, valueMask, failOp, zFailOp, passOp, writeMask
                ];
            }
            return state;
        }

        /**
         * 获取模板状态 逐面设置
         * @param func 正面 比较函数
         * @param ref 正面 模板测试的参考值
         * @param valueMask 正面 在测试时和参考值进行与运算
         * @param failOp 正面 指定模板测试失败时
         * @param zFailOp 正面 指定当模板测试通过但深度测试失败时
         * @param passOp 正面 指定当模板测试和深度测试都通过时
         * @param writeMask 正面 在写入模板缓存时和参考值进行与运算
         * @param backFunc 反面 比较函数
         * @param backRef 反面 模板测试的参考值
         * @param backValueMask 反面 在测试时和参考值进行与运算
         * @param backFailOp 反面 指定模板测试失败时
         * @param backZFailOp 反面 指定当模板测试通过但深度测试失败时
         * @param backPassOp 反面 指定当模板测试和深度测试都通过时
         * @param backWriteMask 反面 在写入模板缓存时和参考值进行与运算
         * @returns 模板状态对象
         */
        public static getStateFace(
            func = CompareFunc.ALWAYS, ref = 0, valueMask = 0xff, failOp = StencilOp.KEEP, zFailOp = StencilOp.KEEP, passOp = StencilOp.KEEP, writeMask = 0xff,
            backFunc = CompareFunc.ALWAYS, backRef = 0, backValueMask = 0xff, backFailOp = StencilOp.KEEP, backZFailOp = StencilOp.KEEP, backPassOp = StencilOp.KEEP, backWriteMask = 0xff
        ) {
            //
            if (ref < 0) ref = 0;
            if (backRef < 0) backRef = 0;
            ref = Math.floor(ref);
            backRef = Math.floor(backRef);
            // eslint-disable-next-line max-len
            let stateKey = `${func}_${ref}_${valueMask}_${failOp}_${zFailOp}_${passOp}_${writeMask}_${backFunc}_${backRef}_${backValueMask}_${backFailOp}_${backZFailOp}_${backPassOp}_${backWriteMask}`;
            let state = this.cacheStateMap[stateKey];
            if (!state) {
                this.cacheStateMap[stateKey] = state = new StencilState();
                state.states = [
                    //front face
                    func, ref, valueMask, failOp, zFailOp, passOp, writeMask,
                    //back face
                    backFunc, backRef, backValueMask, backFailOp, backZFailOp, backPassOp, backWriteMask
                ];
            }
            return state;
        }
    }
}